//: [Previous](@previous)
//: ## 溢出运算符 (Overflow Operator)
// swift的算数运算符出现溢出时,会抛出运行时错误
print(UInt8.min,UInt8.max)//UInt8的U是无符号的意思,那就是没有负数,最小0,最大255
var v1: UInt8 = UInt8.max
//print(v1 += 1) //这里就会报错,因为v1已经是最大值255,再加1放不下
print(v1 &+ 1) //输出:0, UInt8最大值是255,255+1,就只能循环向前就是倒回到原始第一位,想象成一个圆
v1 &* 2 //输出:254 , 可以理解成v1+v1,就是255+255,上面已经证明了255+1=0,所以现在255+255 = 254
//: ## 运算符重载
//类 / 结构体 / 枚举 可以为现有的运算符提供自定义的实现, 这个操作叫做运算符重载
struct Point1 {
    var x: Int, y: Int
}
var p1 = Point1(x: 10, y: 20)
var p2 = Point1(x: 10, y: 20)
//var p3 = p1 + p2 正常情况下,结构体不能直接相加, 不像是单纯的Int

func + (p1: Point1, p2: Point1) -> Point1 {//重载就是方法名保持不变,只有参数类型或者个数改变,要区分开和重写的区别,重写是加上override直接修改内容
    Point1(x: p1.x + p2.x, y: p1.y + p2.y)
}
var p3 = p1 + p2
print(p3)//Point1(x: 20, y: 40)

//这种运算符的重载建议直接写在类型的实现里面
struct Point2 {
    var x: Int, y: Int
    
    static func + (p1: Point2, p2: Point2) -> Point2 {
        Point2(x: p1.x + p2.x, y: p1.y + p2.y)
    }
    
    //prefix: 前缀运算符
    static prefix func - (p: Point2) -> Point2 {
        Point2(x: -p.x, y: -p.y)
    }
    
    //+=的实现, p1+=p2, 其实就是p1 = p1+p2 ,结构体是值类型,是支持直接覆盖的,所以可以使用inout参数,把p1+p2的内容,直接修改成p1就行了
    static func += (p1: inout Point2, p2: Point2) {
        p1 = p1 + p2
    }
    
    //前缀的++ : 当++放在变量的前面, 它会先将变量加1, 然后返回增加后的值
    /*
     int a = 5
     int b = ++a  先将a加一,然后将添加后的值赋值给b
     a:6 b:6
     */
    static prefix func ++ (p: inout Point2) -> Point2 {
        p += Point2(x: 1, y: 1)
        return p
    }
    
    //后缀运算符:postfix
    //后缀的++ : 当++放在变量的后面, 它会先返回变量的当前值, 然后再将变量加一
    /*
     int a = 5
     int b = a++  先将a的值赋值给b, 然后再将a加一
     a:6 b:5
     */
    static postfix func ++ (p: inout Point2) -> Point2 {
        var tmp = p
        p += Point2(x: 1, y: 1)
        return tmp
    }
    
}//在结构体类型的实现里面重载运算符,必须加上static,因为结合现实中外面是直接类就可以调用的直接初始化的时候就可以使用,你不加static就是实例方法,只能p1这种实例去调用就不合理
//前缀运算符,就是放在对象的前面使用的,比如p要是好,就-p, 而普通的运算符一般叫中缀运算符,比如+-这些,就是放在2个对象中间使用的(p+p)
let ppp2 = -Point2(x: 10, y: -20)
print(ppp2) //Point2(x: -10, y: 20)
//: ## Equatable 比较相等协议
/*
 要想得知2个实例是否等价, 一般做法是遵守Equatable协议, 重载 "=="运算符,
 与此同时,也等价于重载了 "!="运算符
 实现了Equatable协议, 就相当于重写了"=="和"!="运算符
 */
class Person1 : Equatable {
    var age: Int
    var name: String
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
    
    static func == (lhs: Person1, rhs: Person1) -> Bool {
        lhs.age == rhs.age
    }//这个是Equatable必须实现的方法, 可以比较所有属性, 也可以根据需求比较个别属性就判定为相等, 反正返回ture了就是相等
}
var per1 = Person1(age: 10, name: "jack")
var per2 = Person1(age: 10, name: "sky")
print(per1 == per2) //true

/*
 Swift为以下类型提供默认的Equatable协议的实现
 1. 没有关联类型的枚举(有原始值也行)
 2. 大部分系统自带的类型,比如Int..
 3. 只拥有遵守Equatable协议关联类型的枚举
 4. 只拥有遵守Equatable协议存储属性的结构体
 5. 类class是肯定不会自动帮类实现的,要自己实现
 */
enum Anwser {
    case right
    case wrong
}
var a1 = Anwser.right
var a2 = Anwser.wrong
a1 == a2 //false
//: ## 恒等运算符 === / !==
/*
 恒等运算符, 3个等号
 只用于 引用类型 比较存储的地址值是否相等(其实就是比较是否指向同一个对象)
 */
print(per1 === per2) //false //这个是2个对象,不是比较内容
print(per1 !== per2) //true
//: ## Comparable 比较大小的协议
/*
 要想比较2个实例的大小, 一般做法是:
 1. 遵守 Comparable协议
 2. 重载相应的运算符
 */
//举例需求: score大的比较大,若score相等,age小的比较大
struct Student : Comparable {
    var score: Int
    var age: Int
    
    static func < (lhs: Student, rhs: Student) -> Bool { //BOOl的含义,真:左边确实比右边小, 假:左边没有比右边小
        /*
        if lhs.score < rhs.score {
            return true
        } else if lhs.score > rhs.score {
            return false
        } else if lhs.score == rhs.score {
            if lhs.age < rhs.age {
                return false
            } else if lhs.age > rhs.age {
                return true
            } else {
                return false
            }
        }
         👆🏻这是我的写法,很累赘还难看明白,👇🏻是老师的写法
         */
        (lhs.score < rhs.score) || (lhs.score == rhs.score)&&(lhs.age > rhs.age)
        /*
         (lhs.score < rhs.score) : 分数小,直接就返回了ture
         (lhs.score == rhs.score)&&(lhs.age > rhs.age) : 分数相等,并且年龄更大,也是ture
         其他就算是false
         */
    }

    static func > (lhs: Student, rhs: Student) -> Bool {
        (lhs.score > rhs.score) || (lhs.score == rhs.score)&&(lhs.age < rhs.age)
    }
    
    static func >= (lhs: Student, rhs: Student) -> Bool {
        !(lhs < rhs) //上面已经实现了<和>, 直接一个非!就解决了
    }
    
    static func <= (lhs: Student, rhs: Student) -> Bool {
        !(lhs > rhs)
    }
}
var stu1 = Student(score: 5, age: 10)
var stu2 = Student(score: 6, age: 10)
print(stu1 < stu2) //true
var stu3 = Student(score: 5, age: 20)
print(stu1 > stu3) //true
//: ## 自定义运算符(Custom Operator)
// 可以自定义新的运算符 : 在全局作用域使用operator(中文:操作人)进行声明
/*
 prefix operator  前缀运算符
 postfix operator 后缀运算符
 infix operator   中缀运算符 : 冒号后要加上优先级组
 */
prefix operator ++++ //自己定义一个运算符++++,表示自加3的意思
prefix func ++++ (_ i: inout Int) {
    i += 3
}
var co1 = 0
++++co1 //3

/*
 中缀运算符(比其他2个不一样的是,需要加个 优先级组)
 precedencegroup 优先级组 {
     associativity: 结合性 (3个选项:left/right/none)(left:从左依次算到右,现实中的习惯. right:从右依次算到左. none:一条运算式中只能出现一次这个运算符,就是只能a+b,不能a+b+c这里就是2次+)
     higherThan: 比谁的优先级高
     lowerThan: 比谁的优先级低,这2个就照抄下面行了,没什么特别
     assignment: true (代表在可选链操作中拥有跟赋值运算符一样的优先级)
 }
 */
precedencegroup PlusMinusPrecedence {
    associativity: none
    higherThan: AdditionPrecedence
    lowerThan: MultiplicationPrecedence
    assignment: true
}
infix operator +- : PlusMinusPrecedence
struct Pinfix {
    var x = 0, y = 0
    //自定义一个运算符,2个结构体的x相+,y相-
    static func +- (p1: Pinfix, p2: Pinfix) -> Pinfix {
        Pinfix(x: p1.x + p2.x, y: p1.y - p2.y)
    }
}
var pi1 = Pinfix(x: 10, y: 20)
var pi2 = Pinfix(x: 5, y: 5)
var pi3 = pi1 +- pi2
print(pi3) //Pinfix(x: 15, y: 15)

class piClass { var age = 0 }
func getPi() -> Int { 10 }
var pc1: piClass? = piClass()
pc1?.age = getPi()
/*
 1. 因为associativity: none  : 
    所以pi1 +- pi2 +- pi3这样子出现了2次运算符,就会报错,需要把none改成left或者right就可以
 2. assignment: true :
    pc1?.age = getPi()这行运算中, 赋值运算符的优先级是什么? 是根据左边可选链的判断再去判断需不需要执行右边. 就是说假如左边可选链为nil, 右边是直接不用运行了的, 不是niu才会运行. 所以assignment: true就是这个意思
 举个栗子:
    var p: class? = class()
    p?.point +- Ponit(x:10, y: 20)
 这个的情况就是+-左边是可选链, 假如p?为nil, 右边的Ponit(x:10, y: 20)就直接不用运行了, 假如p?不为nil, 才有右边的初始化运行和最终的运算操作
 */










//: [Next](@next)
